package com.douyinkan.service.impl;

import com.douyinkan.config.AppConfig;
import com.douyinkan.entity.Content;
import com.douyinkan.mapper.ContentMapper;
import com.douyinkan.util.FileUtil;
import com.douyinkan.util.MediaUtil;
import com.douyinkan.service.FileScanService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.nio.file.StandardWatchEventKinds.*;

@Service
public class FileScanServiceImpl implements FileScanService {
    
    private static final Logger logger = LoggerFactory.getLogger(FileScanServiceImpl.class);
    
    @Autowired
    private AppConfig appConfig;
    
    @Autowired
    private ContentMapper contentMapper;
    
    @Autowired
    private MediaUtil mediaUtil;
    
    private WatchService watchService;
    private Path watchPath;
    private ExecutorService executorService = Executors.newSingleThreadExecutor();
    private boolean isScanningInitialized = false; // 标记扫描是否已初始化

    @PostConstruct
    public void init() {
        logger.info("=== FileScanService initializing ===");
        // 不在启动时自动开始扫描，等待用户选择媒体目录后再开始
        logger.info("FileScanService initialized, waiting for user to select media directory");
        logger.info("=== FileScanService initialization completed ===");
    }
    
    @PreDestroy
    public void destroy() {
        logger.info("=== FileScanService destroying ===");
        stopScanning();
        executorService.shutdown();
        logger.info("=== FileScanService destroyed ===");
    }
    
    /**
     * 启动扫描服务
     * 只有在用户选择媒体目录后才调用此方法
     */
    @Override
    public void startScanning() {
        logger.info("=== Starting file scanning service ===");
        try {
            // 获取当前媒体目录（从应用配置中）
            String mediaDir = appConfig.getContentDir();
            logger.info("Starting file scanning for media directory: {}", mediaDir);
            
            if (mediaDir == null || mediaDir.isEmpty()) {
                logger.warn("Media directory is not set, cannot start scanning");
                return;
            }
            
            // 只初始化一次扫描服务
            if (!isScanningInitialized) {
                watchPath = Paths.get(mediaDir);
                if (!Files.exists(watchPath)) {
                    logger.info("Creating media directory: {}", watchPath);
                    Files.createDirectories(watchPath);
                }
                
                logger.info("Creating watch service for path: {}", watchPath);
                watchService = FileSystems.getDefault().newWatchService();
                watchPath.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
                
                // 启动后台线程处理文件事件
                logger.info("Starting background thread for file event processing");
                executorService.submit(this::processWatchEvents);
                
                // 标记扫描已初始化
                isScanningInitialized = true;
                logger.info("File scanning service initialized successfully");
            } else {
                logger.info("File scanning service already initialized");
            }
            
            // 执行初始扫描
            logger.info("Performing initial directory scan");
            scanDirectory();
            logger.info("=== File scanning service started successfully ===");
        } catch (IOException e) {
            logger.error("Error starting file scanning service: {}", e.getMessage(), e);
        }
    }
    
    @Override
    public void stopScanning() {
        logger.info("=== Stopping file scanning service ===");
        try {
            if (watchService != null) {
                logger.info("Closing watch service");
                watchService.close();
                logger.info("Watch service closed successfully");
            }
        } catch (IOException e) {
            logger.error("Error stopping file scanning service: {}", e.getMessage(), e);
        }
        logger.info("=== File scanning service stopped ===");
    }
    
    @Override
    @Scheduled(fixedRateString = "#{@appConfig.scanInterval * 1000}") // 每隔指定秒数执行
    public void scanDirectory() {
        logger.info("=== Performing scheduled directory scan ===");
        if (watchPath == null) {
            logger.warn("Watch path is not set, skipping directory scan");
            return;
        }
        
        try {
            logger.info("Scanning directory: {}", watchPath);
            Files.walkFileTree(watchPath, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    String filename = file.getFileName().toString();
                    
                    if (FileUtil.isValidMediaFile(filename)) {
                        logger.debug("Processing valid media file: {}", filename);
                        processMediaFile(file, filename);
                    }
                    
                    return FileVisitResult.CONTINUE;
                }
                
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    logger.error("Failed to visit file: {}", file, exc);
                    return FileVisitResult.CONTINUE;
                }
            });
            logger.info("=== Directory scan completed successfully ===");
        } catch (IOException e) {
            logger.error("Error scanning directory: {}", e.getMessage(), e);
        }
    }
    
    @Override
    public void handleFileEvent(WatchEvent.Kind<?> kind, String filename) {
        logger.debug("Handling file event: {} for file: {}", kind.name(), filename);
        Path filePath = watchPath.resolve(filename);
        
        if (kind == ENTRY_CREATE || kind == ENTRY_MODIFY) {
            if (FileUtil.isValidMediaFile(filename)) {
                logger.info("Processing media file event: {} for file: {}", kind.name(), filename);
                processMediaFile(filePath, filename);
            }
        } else if (kind == ENTRY_DELETE) {
            // 从数据库中删除对应的记录
            Content content = contentMapper.findByFilename(filename);
            if (content != null) {
                logger.info("Deleting content from database: {}", filename);
                contentMapper.deleteById(content.getId());
                logger.info("Deleted content from database: {}", filename);
            }
        }
    }
    
    private void processMediaFile(Path filePath, String filename) {
        logger.debug("Processing media file: {}", filename);
        // 检查是否已经存在于数据库中
        if (contentMapper.findByFilename(filename) != null) {
            logger.debug("File already exists in database, skipping: {}", filename);
            return; // 已存在，跳过
        }
        
        try {
            Content content = new Content();
            content.setFilename(filename);
            content.setFilePath(filePath.toString());
            content.setContentType(FileUtil.getContentType(filename));
            
            // 提取媒体信息
            BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
            content.setFileSizeBytes(attrs.size());
            
            // 根据文件类型提取更多信息
            if (FileUtil.isVideoFile(filename)) {
                logger.info("Extracting video metadata for: {}", filename);
                MediaUtil.MediaInfo info = mediaUtil.extractVideoInfo(filePath.toString());
                content.setDurationSeconds(info.getDuration());
                content.setWidth(info.getWidth());
                content.setHeight(info.getHeight());
            } else if (FileUtil.isImageFile(filename)) {
                logger.info("Extracting image metadata for: {}", filename);
                MediaUtil.MediaInfo info = mediaUtil.extractImageInfo(filePath.toString());
                content.setWidth(info.getWidth());
                content.setHeight(info.getHeight());
            }
            
            content.setTitle(FileUtil.getFilenameWithoutExtension(filename));
            content.setCreatedAt(LocalDateTime.now());
            content.setUpdatedAt(LocalDateTime.now());
            
            // 保存到数据库
            contentMapper.insert(content);
            logger.info("Added new content to database: {}", filename);
        } catch (IOException e) {
            logger.error("Error processing media file: {}", filename, e);
        }
    }
    
    private void processWatchEvents() {
        logger.info("=== Starting file event processor ===");
        WatchKey key;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                key = watchService.take();
                
                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();
                    
                    if (kind == OVERFLOW) {
                        logger.warn("Overflow event detected, ignoring");
                        continue; // 忽略溢出事件
                    }
                    
                    @SuppressWarnings("unchecked")
                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    Path filename = ev.context();
                    logger.debug("File event detected: {} for file: {}", kind.name(), filename);
                    handleFileEvent(kind, filename.toString());
                }
                
                // 重置键，准备接收下次事件
                boolean valid = key.reset();
                if (!valid) {
                    logger.warn("Watch key is invalid, breaking event loop");
                    break; // 键无效，退出循环
                }
            } catch (InterruptedException e) {
                logger.info("File event processor interrupted");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                logger.error("Error processing file events: {}", e.getMessage(), e);
            }
        }
        logger.info("=== File event processor stopped ===");
    }
}